#!/usr/bin/env python
# Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.

import subprocess
import time
import utils

def install_apk_on_emulator(apk, emulator_id, quiet=False):
  cmd = ['adb', '-s', emulator_id, 'install', '-r', '-d', apk]
  if quiet:
    subprocess.check_output(cmd)
  else:
    subprocess.check_call(cmd)


def uninstall_apk_on_emulator(app_id, emulator_id):
  process = subprocess.Popen(
    ['adb', '-s', emulator_id, 'uninstall', app_id],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  stdout, stderr = process.communicate()
  stdout = stdout.decode('UTF-8')
  stderr = stderr.decode('UTF-8')

  if stdout.strip() == 'Success':
    # Successfully uninstalled
    return

  if 'Unknown package: {}'.format(app_id) in stderr:
    # Application not installed
    return

  # Check if the app is listed in packages
  packages = subprocess.check_output(['adb', 'shell', 'pm', 'list', 'packages'])
  if not 'package:' + app_id in packages:
    return

  raise Exception(
    'Unexpected result from `adb uninstall {}\nStdout: {}\nStderr: {}'.format(
      app_id, stdout, stderr))


def wait_for_emulator(emulator_id):
  stdout = subprocess.check_output(['adb', 'devices']).decode('UTF-8')
  if '{}\tdevice'.format(emulator_id) in stdout:
    return True

  print('Emulator \'{}\' not connected; waiting for connection'.format(
    emulator_id))

  time_waited = 0
  while True:
    time.sleep(10)
    time_waited += 10
    stdout = subprocess.check_output(['adb', 'devices']).decode('UTF-8')
    if '{}\tdevice'.format(emulator_id) not in stdout:
      print('... still waiting for connection')
      if time_waited >= 5 * 60:
        return False
    else:
      return True


def run_monkey(app_id, emulator_id, apk, monkey_events, quiet, enable_logging):
  if not wait_for_emulator(emulator_id):
    return False

  install_apk_on_emulator(apk, emulator_id, quiet)

  # Intentionally using a constant seed such that the monkey generates the same
  # event sequence for each shrinker.
  random_seed = 42

  cmd = ['adb', '-s', emulator_id, 'shell', 'monkey', '-p', app_id,
         '-s', str(random_seed), str(monkey_events)]

  try:
    stdout = utils.RunCmd(cmd, quiet=quiet, logging=enable_logging)
    succeeded = ('Events injected: {}'.format(monkey_events) in stdout)
  except subprocess.CalledProcessError as e:
    succeeded = False

  uninstall_apk_on_emulator(app_id, emulator_id)

  return succeeded


def run_instrumented(app_id, test_id, emulator_id, apk, test_apk, quiet,
                     enable_logging,
                     test_runner='androidx.test.runner.AndroidJUnitRunner'):
  if not wait_for_emulator(emulator_id):
    return None

  install_apk_on_emulator(apk, emulator_id, quiet)
  install_apk_on_emulator(test_apk, emulator_id, quiet)

  cmd = ['adb', '-s', emulator_id, 'shell', 'am', 'instrument', '-w',
         '{}/{}'.format(test_id, test_runner)]

  try:
    stdout = utils.RunCmd(cmd, quiet=quiet, logging=enable_logging)
    # The runner will print OK (X tests) if completed succesfully
    succeeded = any("OK (" in s for s in stdout)
  except subprocess.CalledProcessError as e:
    succeeded = False

  uninstall_apk_on_emulator(test_id, emulator_id)
  uninstall_apk_on_emulator(app_id, emulator_id)

  return succeeded
